#include "client.h"

int recvn(int sockfd, void *buf, int length){
    int total = 0;
    char *p = (char *)buf;
    while(total < length){
        ssize_t sret = recv(sockfd,p+total,length-total,0);
        total += sret;
    }
    return total;
}

int recvPWD(int netfd){
    train_t train;
    recvn(netfd,&train,sizeof(train));
    ERROR_CHECK(train.tag,!PWD,"recvPWD");
    printf("%s\n",train.data);
    return 0;
}

int recvLS(int netfd){
    train_t train;
    while(1){
        recvn(netfd,&train,sizeof(train));
        if(train.tag == FILEEND){
            break;
        }
        printf("%s\n",train.data);
    }
    return 0;
}

//hash[65]
int getHash(char* filename,char* hash){
    int fd  = open(filename, O_RDWR);
    if (fd == -1) {
        perror("Failed to open file");
        return -1;
    }

    SHA256_CTX sha256;
    SHA256_Init(&sha256);

    unsigned char buffer[4096];

    while (1) {
        bzero(buffer,4096);
        ssize_t sret = read(fd,buffer,sizeof(buffer));
        if(sret == 0){
            break;
        }
        SHA256_Update(&sha256, buffer, sret);
    }

    unsigned char md[SHA256_DIGEST_LENGTH];
    SHA256_Final(md, &sha256);
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(hash + (i * 2), "%02x", md[i]); // %02x 表示 2 位十六进制，不足补零
    }
    hash[64] = '\0';
    close(fd);
    return 0;
}

int putfile_c_to_s(int netfd,char* filename){
    train_t train;
    char hash[65] = {0};
    getHash(filename,hash);

    train.tag = FILENAME;
    train.length = strlen(filename) + 1;
    memcpy(train.data,filename,train.length);
    int fd = open(filename, O_RDWR);
    if(fd == -1){
        perror("open in putFile.");
        return -1;
    }
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);

    struct stat statbuf;
    fstat(fd, &statbuf);
    off_t fileSize = statbuf.st_size;
    train.tag = FILESIZE;
    train.length = sizeof(fileSize);
    memcpy(&train.data,&fileSize,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    //发送哈希
    train.tag = FILENAME;
    train.length = sizeof(hash) + 1;
    memcpy(&train.data,&hash,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    //收到回复
    recvn(netfd,&train,sizeof(train));
    if(train.tag == 0){
        printf("send %s, fileszie = %ld.\n",filename,fileSize);
        close(fd);
        return 0;
    }

    off_t offSet;
    recvn(netfd,&train,sizeof(train));
    memcpy(&offSet,train.data,train.length);
    printf("offSet = %ld\n",offSet);

    if(fileSize < THRESHOLD){
        lseek(fd,offSet,SEEK_SET);
        while(1){
            bzero(&train,sizeof(train));
            train.tag = FILEDATA;
            int ret = read(fd,train.data,sizeof(train.data));
            if(ret == 0){
                break;
            }
            train.length = ret;
            send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        }
        train.tag = FILEEND;
        train.length = 0;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        printf("send %s, fileszie = %ld.\n",filename,fileSize);
    }else{
        char* p = (char *)mmap(NULL,fileSize,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
        train.tag = FILEDATA;
        while(offSet < fileSize){
            if(fileSize - offSet > 1000){
                train.length = 1000;
            }else{
                train.length = fileSize - offSet;
            }
            memcpy(train.data,p+offSet,train.length);
            send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
            offSet += train.length;
        }
        train.tag = FILEEND;
        train.length = 0;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        printf("send %s, fileszie = %ld.\n",filename,fileSize);
        munmap(p,fileSize);
    }
    close(fd);
    recvn(netfd,&train,sizeof(train));
    return train.tag;
}

int getfile_s_to_c(int netfd){
    train_t train;
    recvn(netfd,&train,sizeof(train));
    if(train.tag != FILENAME){
        printf("recv filename error in getFile.\n");
        return -1;
    }
    char fileName[4096] = {0};
    memcpy(fileName, train.data,train.length);

    off_t fileSize;
    recvn(netfd,&train,sizeof(train));
    if(train.tag != FILESIZE){
        printf("recv filesize error in getFile.\n");
        return -1;
    }
    memcpy(&fileSize,train.data,train.length);

    char nickname[4096] = {0};
    strcat(nickname,fileName);
    strcat(nickname,".filepart");

    off_t offSet;
    int fd = open(nickname, O_RDWR | O_CREAT | O_APPEND, 0666);
    struct stat statbuf;
    fstat(fd,&statbuf);
    offSet = statbuf.st_size;

    train.tag = FILESIZE;
    train.length = sizeof(offSet);
    memcpy(train.data,&offSet,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    lseek(fd,offSet,SEEK_SET);

    off_t curSize = offSet;
    off_t lastSize = offSet;
    off_t slice = fileSize / 1000;
    while(1){
        recvn(netfd,&train,sizeof(train));
        if(train.tag == FILEEND){
            break;
        }
        curSize += train.length;
        if(curSize - lastSize > slice){
            printf("%5.2lf%%\r", 100.0*curSize/fileSize);
            fflush(stdout); //手动刷新缓冲区
            lastSize = curSize;
        }
        write(fd,train.data,train.length);
    }
    rename(nickname,fileName);
    printf("100.00%%\n");
    printf("%s, filesize = %ld, transfer completed.\n",fileName,fileSize);
    close(fd);
    return 0;
}
